part of '../page.dart';

class _Slider extends StatefulWidget {
  const _Slider({
    required this.tabBuilder,
    required this.dotCount,
    required this.tabController,
    required this.nextButtonTitle,
    required this.finishButtonText,
    required this.onFinishButtonPressed,
  });

  final TabController tabController;
  final String nextButtonTitle;
  final String finishButtonText;
  final int dotCount;
  final Widget Function(int index) tabBuilder;
  final VoidCallback onFinishButtonPressed;

  @override
  _SliderState createState() => _SliderState();
}

class _SliderState extends State<_Slider> with ScreenUtilMixin {
  static const _dotSliderAnimation = _DotSliderAnimation.sizeTransition;
  static const _dotsShown = true;

  late List<Widget> _dots;
  late List<double> _dotSize;
  late List<double> _dotOpacity;

  // For DotSliderAnimation.dotMovement
  late double _marginLeftDotFocused;
  late double _marginRightDotFocused;
  late double _initValueMarginRight;

  // For DotSliderAnimation.sizeTransition
  late double _currentAnimationValue;
  late double _lastKnownAnimationValue;
  late int _currentTabIndex;

  double get _effectiveDotSize {
    return 11 * defaultScaleFactor;
  }

  @override
  void initState() {
    super.initState();
    _dots = [];
    _dotSize = [];
    _dotOpacity = [];
    _marginLeftDotFocused = 0;
    _marginRightDotFocused = 0;
    _initValueMarginRight = (_effectiveDotSize * 2) * (widget.dotCount - 1);
    _lastKnownAnimationValue = 0;
    _currentAnimationValue = 0;
    _currentTabIndex = 0;

    if (_dotSliderAnimation == _DotSliderAnimation.dotMovement) {
      for (var i = 0; i < widget.dotCount; i++) {
        _dotSize.add(_effectiveDotSize);
        _dotOpacity.add(1.0);
      }

      _marginRightDotFocused = _initValueMarginRight;
    } else if (_dotSliderAnimation == _DotSliderAnimation.sizeTransition) {
      for (var i = 0; i < widget.dotCount; i++) {
        if (i == 0) {
          _dotSize.add(_effectiveDotSize * 1.5);
          _dotOpacity.add(1.0);
        } else {
          _dotSize.add(_effectiveDotSize);
          _dotOpacity.add(0.5);
        }
      }
    }

    widget.tabController.addListener(_onTabChanged);
    widget.tabController.animation!.addListener(_onTabChanging);
  }

  @override
  void didUpdateWidget(_Slider oldWidget) {
    super.didUpdateWidget(oldWidget);

    if (widget.tabController.length == oldWidget.tabController.length) {
      return;
    }

    oldWidget.tabController.removeListener(_onTabChanged);
    oldWidget.tabController.animation!.removeListener(_onTabChanging);
    widget.tabController.addListener(_onTabChanged);
    widget.tabController.animation!.addListener(_onTabChanging);

    _dotSize = [];
    _dotOpacity = [];
    _initValueMarginRight = (_effectiveDotSize * 2) * (widget.dotCount - 1);

    if (_dotSliderAnimation == _DotSliderAnimation.dotMovement) {
      for (var i = 0; i < widget.dotCount; i++) {
        _dotSize.add(_effectiveDotSize);
        _dotOpacity.add(1.0);
      }
    } else if (_dotSliderAnimation == _DotSliderAnimation.sizeTransition) {
      for (var i = 0; i < widget.dotCount; i++) {
        if (i == _currentTabIndex) {
          _dotSize.add(_effectiveDotSize * 1.5);
          _dotOpacity.add(1.0);
        } else {
          _dotSize.add(_effectiveDotSize);
          _dotOpacity.add(0.5);
        }
      }
    }

    _updateDots();
  }

  @override
  void dispose() {
    widget.tabController.animation!.removeListener(_onTabChanging);
    widget.tabController.removeListener(_onTabChanged);
    super.dispose();
  }

  void _onTabChanged() {
    if (widget.tabController.indexIsChanging) {
      _currentTabIndex = widget.tabController.previousIndex;
    } else {
      _currentTabIndex = widget.tabController.index;
    }
    _currentAnimationValue = _lastKnownAnimationValue;
  }

  void _updateDots() {
    switch (_dotSliderAnimation) {
      case _DotSliderAnimation.dotMovement:
        _marginLeftDotFocused = _lastKnownAnimationValue * _effectiveDotSize * 2;
        _marginRightDotFocused =
            _initValueMarginRight - _lastKnownAnimationValue * _effectiveDotSize * 2;
      case _DotSliderAnimation.sizeTransition:
        if (_lastKnownAnimationValue == _currentAnimationValue) {
          break;
        }

        var diffValueAnimation = (_lastKnownAnimationValue - _currentAnimationValue).abs();
        final diffValueIndex = (_currentTabIndex - widget.tabController.index).abs();

        // When press skip button
        if (widget.tabController.indexIsChanging &&
            (widget.tabController.index - widget.tabController.previousIndex).abs() > 1) {
          if (diffValueAnimation < 1.0) {
            diffValueAnimation = 1.0;
          }
          _dotSize[_currentTabIndex] = _effectiveDotSize * 1.5 -
              (_effectiveDotSize / 2) * (1 - (diffValueIndex - diffValueAnimation));
          _dotSize[widget.tabController.index] = _effectiveDotSize +
              (_effectiveDotSize / 2) * (1 - (diffValueIndex - diffValueAnimation));
          _dotOpacity[_currentTabIndex] = 1.0 - (diffValueAnimation / diffValueIndex) / 2;
          _dotOpacity[widget.tabController.index] = 0.5 + (diffValueAnimation / diffValueIndex) / 2;
        } else {
          if (_lastKnownAnimationValue > _currentAnimationValue) {
            // Swipe left
            _dotSize[_currentTabIndex] =
                _effectiveDotSize * 1.5 - (_effectiveDotSize / 2) * diffValueAnimation;
            _dotSize[_currentTabIndex + 1] =
                _effectiveDotSize + (_effectiveDotSize / 2) * diffValueAnimation;
            _dotOpacity[_currentTabIndex] = 1.0 - diffValueAnimation / 2;
            _dotOpacity[_currentTabIndex + 1] = 0.5 + diffValueAnimation / 2;
          } else {
            // Swipe right
            _dotSize[_currentTabIndex] =
                _effectiveDotSize * 1.5 - (_effectiveDotSize / 2) * diffValueAnimation;
            _dotSize[_currentTabIndex - 1] =
                _effectiveDotSize + (_effectiveDotSize / 2) * diffValueAnimation;
            _dotOpacity[_currentTabIndex] = 1.0 - diffValueAnimation / 2;
            _dotOpacity[_currentTabIndex - 1] = 0.5 + diffValueAnimation / 2;
          }
        }
    }
  }

  void _onTabChanging() {
    setState(() {
      _lastKnownAnimationValue = widget.tabController.animation!.value;
      _updateDots();
    });
  }

  bool get _isAnimating {
    return _lastKnownAnimationValue - _lastKnownAnimationValue.truncate() != 0;
  }

  bool get _isLastTab {
    return (widget.dotCount - 1 - _lastKnownAnimationValue) < 0.5;
  }

  @override
  Widget build(BuildContext context) {
    return IgnorePointer(
      ignoring: _isAnimating,
      child: Stack(
        children: [
          TabBarView(
            controller: widget.tabController,
            physics: const BouncingScrollPhysics(),
            children: List.generate(
              widget.tabController.length,
              (index) {
                return widget.tabBuilder(index);
              },
            ),
          ),
          _buildBottom(),
        ],
      ),
    );
  }

  void _onNextButtonPressed() {
    if (_isLastTab) {
      widget.onFinishButtonPressed();
    } else {
      widget.tabController.animateTo(
        widget.tabController.index + 1,
        curve: Curves.easeOut,
      );
    }
  }

  Widget _buildBottom() {
    return Align(
      alignment: Alignment.bottomCenter,
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          if (_dotsShown) ...[
            SizedBox(
              height: _effectiveDotSize * 1.5,
              child: Stack(
                children: [
                  Positioned.fill(
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: _buildDots(),
                    ),
                  ),
                  if (_dotSliderAnimation == _DotSliderAnimation.dotMovement)
                    Center(
                      child: Container(
                        decoration: BoxDecoration(
                          color: CoreTheme.of(context).primaryColor,
                          borderRadius: BorderRadius.circular(_effectiveDotSize / 2),
                        ),
                        width: _effectiveDotSize,
                        height: _effectiveDotSize,
                        margin: EdgeInsets.only(
                          left: _marginLeftDotFocused,
                          right: _marginRightDotFocused,
                        ),
                      ),
                    ),
                ],
              ),
            ),
            SizedBox(
              height: 16 * logWidthScaleFactor,
            ),
          ],
          SafeArea(
            left: false,
            top: false,
            right: false,
            bottom: true,
            child: Padding(
              padding: EdgeInsets.symmetric(
                horizontal: 20 * logWidthScaleFactor,
              ),
              child: OverlayTextButton(
                onPressed: _onNextButtonPressed,
                title: _isLastTab ? widget.finishButtonText : widget.nextButtonTitle,
              ),
            ),
          ),
          SizedBox(
            height: 20 * logWidthScaleFactor,
          ),
        ],
      ),
    );
  }

  List<Widget> _buildDots() {
    _dots.clear();
    for (var i = 0; i < widget.dotCount; i++) {
      _dots.add(_buildDot(size: _dotSize[i], opacity: _dotOpacity[i]));
    }
    return _dots;
  }

  Widget _buildDot({
    required double size,
    required double opacity,
  }) {
    return Opacity(
      opacity: opacity,
      child: Container(
        decoration: BoxDecoration(
          color: CoreTheme.of(context).primaryColor,
          borderRadius: BorderRadius.circular(size / 2),
        ),
        width: size,
        height: size,
        margin: EdgeInsets.only(
          left: size / 2,
          right: size / 2,
        ),
      ),
    );
  }
}

enum _DotSliderAnimation {
  sizeTransition,
  dotMovement,
}
